 /*******************************************************************************
  * Copyright (c) 2000, 2007 IBM Corporation and others.
  * All rights reserved. This program and the accompanying materials
  * are made available under the terms of the Eclipse Public License v1.0
  * which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/epl-v10.html
  *
  * Contributors:
  * IBM Corporation - initial API and implementation
  *******************************************************************************/
 package org.eclipse.ui.internal;

 import java.lang.reflect.InvocationTargetException ;
 import java.lang.reflect.Method ;
 import java.util.ArrayList ;
 import java.util.Iterator ;
 import java.util.List ;

 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.Platform;
 import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.StructuredSelection;
 import org.eclipse.ui.internal.util.BundleUtility;
 import org.osgi.framework.Bundle;

 /**
  * Provides access to resource-specific classes, needed to provide
  * backwards compatibility for resource-specific functions which
  * could not be moved up from the generic workbench layer to the
  * IDE layer.
  */
 public final class LegacyResourceSupport {

     private static String [] resourceClassNames = {
         "org.eclipse.core.resources.IResource", //$NON-NLS-1$
 "org.eclipse.core.resources.IContainer", //$NON-NLS-1$
 "org.eclipse.core.resources.IFolder", //$NON-NLS-1$
 "org.eclipse.core.resources.IProject", //$NON-NLS-1$
 "org.eclipse.core.resources.IFile", //$NON-NLS-1$
 };
     
     /**
      * Cached value of
      * <code>Class.forName("org.eclipse.core.resources.IResource")</code>;
      * <code>null</code> if not initialized or not present.
      * @since 3.0
      */
     private static Class iresourceClass = null;

     /**
      * Cached value of
      * <code>Class.forName("org.eclipse.core.resources.IFile")</code>;
      * <code>null</code> if not initialized or not present.
      * @since 3.1
      */
     private static Class ifileClass;
     
     /**
      * Cached value of
      * <code>Class.forName("org.eclipse.ui.IContributorResourceAdapter")</code>;
      * <code>null</code> if not initialized or not present.
      * @since 3.0
      */
     private static Class icontributorResourceAdapterClass = null;

     /**
      * Cached value of </code> org.eclipse.ui.IContributorResourceAdapter.getAdaptedResource(IAdaptable) </code>
      * <code>null</code> if not initialized or not present.
      *
      * @since 3.3
      */
     private static Method getAdaptedResourceMethod = null;
     
     /**
      * Cached value of </code> org.eclipse.ui.IContributorResourceAdapter2.getAdaptedResourceMapping(IAdaptable) </code>
      * <code>null</code> if not initialized or not present.
      *
      * @since 3.3
      */
     private static Method getAdaptedResourceMappingMethod = null;
     
     /**
      * Cached value of
      * <code>Class.forName("org.eclipse.ui.ide.IContributorResourceAdapter2")</code>;
      * <code>null</code> if not initialized or not present.
      * @since 3.1
      */
     private static Class icontributorResourceAdapter2Class = null;
     
     /**
      * Cached value of
      * <code>Class.forName("org.eclipse.ui.internal.ide.DefaultContributorResourceAdapter")</code>;
      * <code>null</code> if not initialized or not present.
      * @since 3.0
      */
     private static Class defaultContributorResourceAdapterClass = null;

     /**
      * Cached value for reflective result of <code>DefaultContributorRessourceAdapter.getDefault()</code>.
      * <code>null</code> if not initialized or not present.
      *
      * @since 3.3
      */
     private static Object defaultContributorResourceAdapter = null;
     
     /**
      * Cached value of
      * <code>Class.forName("org.eclipse.core.resources.mapping.ResourceMappingr")</code>;
      * <code>null</code> if not initialized or not present.
      * @since 3.0
      */
     private static Class resourceMappingClass = null;

     /**
      * Indicates whether the IDE plug-in (which supplies the
      * resource contribution adapters) is even around.
      */
     private static boolean resourceAdapterPossible = true;


     /**
      * Returns <code>IFile.class</code> or <code>null</code> if the
      * class is not available.
      * <p>
      * This method exists to avoid explicit references from the generic
      * workbench to the resources plug-in.
      * </p>
      *
      * @return <code>IFile.class</code> or <code>null</code> if class
      * not available
      * @since 3.1
      */
     public static Class getFileClass() {
         if (ifileClass != null) {
             // tried before and succeeded
 return ifileClass;
         }
         Class c = loadClass("org.eclipse.core.resources", "org.eclipse.core.resources.IFile"); //$NON-NLS-1$ //$NON-NLS-2$
 if (c != null) {
             // The class was found so record it
 ifileClass = c;
         }
         return c;
     }

     /**
      * Returns <code>IResource.class</code> or <code>null</code> if the
      * class is not available.
      * <p>
      * This method exists to avoid explicit references from the generic
      * workbench to the resources plug-in.
      * </p>
      *
      * @return <code>IResource.class</code> or <code>null</code> if class
      * not available
      * @since 3.0
      */
     public static Class getResourceClass() {
         if (iresourceClass != null) {
             // tried before and succeeded
 return iresourceClass;
         }
         Class c = loadClass("org.eclipse.core.resources", "org.eclipse.core.resources.IResource"); //$NON-NLS-1$ //$NON-NLS-2$
 if (c != null) {
             // The class was found so record it
 iresourceClass = c;
         }
         return c;
     }

     /**
      * Returns <code>ResourceMapping.class</code> or <code>null</code> if the
      * class is not available.
      * <p>
      * This method exists to avoid explicit references from the generic
      * workbench to the resources plug-in.
      * </p>
      *
      * @return <code>ResourceMapping.class</code> or <code>null</code> if class
      * not available
      * @since 3.1
      */
     public static Class getResourceMappingClass() {
         if (resourceMappingClass != null) {
             // tried before and succeeded
 return resourceMappingClass;
         }
         Class c = loadClass("org.eclipse.core.resources", "org.eclipse.core.resources.mapping.ResourceMapping"); //$NON-NLS-1$ //$NON-NLS-2$
 if (c != null) {
             // The class was found so record it
 resourceMappingClass = c;
         }
         return c;
     }
     
     /**
      * Returns <code>IContributorResourceAdapter.class</code> or
      * <code>null</code> if the class is not available.
      * <p>
      * This method exists to avoid explicit references from the generic
      * workbench to the IDE plug-in.
      * </p>
      *
      * @return <code>IContributorResourceAdapter.class</code> or
      * <code>null</code> if class not available
      * @since 3.0
      */
     public static Class getIContributorResourceAdapterClass() {
         if (icontributorResourceAdapterClass != null) {
             // tried before and succeeded
 return icontributorResourceAdapterClass;
         }
         Class c = loadClass("org.eclipse.ui.ide", "org.eclipse.ui.IContributorResourceAdapter"); //$NON-NLS-1$ //$NON-NLS-2$
 if (c != null) {
             // The class was found so record it
 icontributorResourceAdapterClass = c;
         }
         return c;
     }

     /**
      * Returns <code>IContributorResourceAdapter2.class</code> or
      * <code>null</code> if the class is not available.
      * <p>
      * This method exists to avoid explicit references from the generic
      * workbench to the IDE plug-in.
      * </p>
      *
      * @return <code>IContributorResourceAdapter.class</code> or
      * <code>null</code> if class not available
      * @since 3.1
      */
     public static Class getIContributorResourceAdapter2Class() {
         if (icontributorResourceAdapter2Class != null) {
             // tried before and succeeded
 return icontributorResourceAdapter2Class;
         }
         Class c = loadClass("org.eclipse.ui.ide", "org.eclipse.ui.ide.IContributorResourceAdapter2"); //$NON-NLS-1$ //$NON-NLS-2$
 if (c != null) {
             // The class was found so record it
 icontributorResourceAdapter2Class = c;
         }
         return c;
     }
     
     private static Class loadClass(String bundleName, String className) {
         if (!resourceAdapterPossible) {
             // tried before and failed
 return null;
         }
         Bundle bundle = Platform.getBundle(bundleName);
         if (bundle == null) {
             // Required plug-in is not around
 // assume that it will never be around
 resourceAdapterPossible = false;
             return null;
         }
         // Required plug-in is around
 // it's not our job to activate the plug-in
 if (!BundleUtility.isActivated(bundle)) {
             // assume it might come alive later
 resourceAdapterPossible = true;
             return null;
         }
         try {
             return bundle.loadClass(className);
         } catch (ClassNotFoundException e) {
             // unable to load the class - sounds pretty serious
 // treat as if the plug-in were unavailable
 resourceAdapterPossible = false;
             return null;
         }
     }
     
     /**
      * Returns <code>DefaultContributorResourceAdapter.class</code> or
      * <code>null</code> if the class is not available.
      * <p>
      * This method exists to avoid explicit references from the generic
      * workbench to the IDE plug-in.
      * </p>
      *
      * @return <code>DefaultContributorResourceAdapter.class</code> or
      * <code>null</code> if class not available
      * @since 3.0
      */
     public static Class getDefaultContributorResourceAdapterClass() {
         if (defaultContributorResourceAdapterClass != null) {
             // tried before and succeeded
 return defaultContributorResourceAdapterClass;
         }
         Class c = loadClass("org.eclipse.ui.ide", "org.eclipse.ui.internal.ide.DefaultContributorResourceAdapter"); //$NON-NLS-1$ //$NON-NLS-2$
 if (c != null) {
             // The class was found so record it
 defaultContributorResourceAdapterClass = c;
         }
         return c;
     }
     
     private static Object getDefaultContributorResourceAdapter() {
         if (defaultContributorResourceAdapter != null) {
             return defaultContributorResourceAdapter;
         }
         
         // reflective equivalent of
 // resourceAdapter = DefaultContributorResourceAdapter.getDefault();

             Class c = LegacyResourceSupport.getDefaultContributorResourceAdapterClass();
             if (c != null) {
                 try {
                     Method m = c.getDeclaredMethod("getDefault", new Class [0]);//$NON-NLS-1$
 defaultContributorResourceAdapter = m.invoke(null, new Object [0]);
                     return defaultContributorResourceAdapter;
                 } catch (SecurityException e) {
                     // shouldn't happen - but play it safe
 } catch (NoSuchMethodException e) {
                     // shouldn't happen - but play it safe
 } catch (IllegalArgumentException e) {
                     // shouldn't happen - but play it safe
 } catch (IllegalAccessException e) {
                     // shouldn't happen - but play it safe
 } catch (InvocationTargetException e) {
                     // shouldn't happen - but play it safe
 }
                 
                 
             }
         
         return null;

     }
     
     /**
      * Returns <code>true</code> if the provided type name is an
      * <code>IResource</code>, and <code>false</code> otherwise.
      * @param objectClassName
      * @return <code>true</code> if the provided type name is an
      * <code>IResource</code>, and <code>false</code> otherwise.
      *
      * @since 3.1
      */
     public static boolean isResourceType(String objectClassName) {
         for (int i = 0; i < resourceClassNames.length; i++) {
             if (resourceClassNames[i].equals(objectClassName)) {
                 return true;
             }
         }
         return false;
     }
     
     /**
      * Returns <code>true</code> if the provided type name is an
      * <code>"org.eclipse.core.resources.mapping.ResourceMapping"</code>, and <code>false</code> otherwise.
      * @param objectClassName
      * @return <code>true</code> if the provided type name is an
      * <code>"org.eclipse.core.resources.mapping.ResourceMapping"</code>, and <code>false</code> otherwise.
      *
      * @since 3.1
      */
     public static boolean isResourceMappingType(String objectClassName) {
         return objectClassName.equals("org.eclipse.core.resources.mapping.ResourceMapping"); //$NON-NLS-1$
 }
     
     /**
      * Returns the class search order starting with <code>extensibleClass</code>.
      * The search order is defined in this class' comment.
      *
      * @since 3.1
      */
     private static boolean isInstanceOf(Class clazz, String type) {
         if (clazz.getName().equals(type)) {
             return true;
         }
         Class superClass= clazz.getSuperclass();
         if (superClass != null && isInstanceOf(superClass, type)) {
             return true;
         }
         Class [] interfaces= clazz.getInterfaces();
         for (int i= 0; i < interfaces.length; i++) {
             if (isInstanceOf(interfaces[i], type)) {
                 return true;
             }
         }
         return false;
     }
     
     /**
      * Returns the adapted resource using the <code>IContributorResourceAdapter</code>
      * registered for the given object. If the Resources plug-in is not loaded
      * the object can not be adapted.
      *
      * @param object the object to adapt to <code>IResource</code>.
      * @return returns the adapted resource using the <code>IContributorResourceAdapter</code>
      * or <code>null</code> if the Resources plug-in is not loaded.
      *
      * @since 3.1
      */
     public static Object getAdaptedContributorResource(Object object) {
         Class resourceClass = LegacyResourceSupport.getResourceClass();
         if (resourceClass == null) {
             return null;
         }
         if (resourceClass.isInstance(object)) {
             return null;
         }
         if (object instanceof IAdaptable) {
             IAdaptable adaptable = (IAdaptable) object;
             Class contributorResourceAdapterClass = LegacyResourceSupport.getIContributorResourceAdapterClass();
             if (contributorResourceAdapterClass == null) {
                 return adaptable.getAdapter(resourceClass);
             }
             Object resourceAdapter = adaptable.getAdapter(contributorResourceAdapterClass);
             if (resourceAdapter == null) {
                 resourceAdapter = LegacyResourceSupport.getDefaultContributorResourceAdapter();
                 if (resourceAdapter == null) {
                     return null;
                 }
             }
             // reflective equivalent of
 // result = ((IContributorResourceAdapter) resourceAdapter).getAdaptedResource(adaptable);

                 Method m = getContributorResourceAdapterGetAdaptedResourceMethod();
                 if (m != null) {
                     try {
                         return m.invoke(resourceAdapter, new Object []{adaptable});
                     } catch (IllegalArgumentException e) {
                         // shouldn't happen - but play it safe
 } catch (IllegalAccessException e) {
                         // shouldn't happen - but play it safe
 } catch (InvocationTargetException e) {
                         // shouldn't happen - but play it safe
 }
                 }
             
         }
         return null;
     }
     
     private static Method getContributorResourceAdapterGetAdaptedResourceMethod() {
         if (getAdaptedResourceMethod != null) {
             return getAdaptedResourceMethod;
         }
       
         Class c = getIContributorResourceAdapterClass();
         if (c != null) {
             try {
                 getAdaptedResourceMethod = c.getDeclaredMethod("getAdaptedResource", new Class []{IAdaptable.class}); //$NON-NLS-1$
 return getAdaptedResourceMethod;
             } catch (SecurityException e) {
                 // shouldn't happen - but play it safe
 } catch (NoSuchMethodException e) {
                 // shouldn't happen - but play it safe
 }
                 
         }
         
         return null;
     }


     private static Method getContributorResourceAdapter2GetAdaptedResourceMappingMethod() {
         if (getAdaptedResourceMappingMethod != null) {
             return getAdaptedResourceMappingMethod;
         }
        
         Class c = getIContributorResourceAdapter2Class();
         if (c != null) {
             try {
                 getAdaptedResourceMappingMethod = c.getDeclaredMethod("getAdaptedResourceMapping", new Class []{IAdaptable.class}); //$NON-NLS-1$
 return getAdaptedResourceMappingMethod;
             } catch (SecurityException e) {
                 // do nothing - play it safe
 } catch (NoSuchMethodException e) {
                 // do nothing - play it safe
 }
                 
         }
         
         return null;
     }

     /**
      * Returns the adapted resource mapping using the <code>IContributorResourceAdapter2</code>
      * registered for the given object. If the Resources plug-in is not loaded
      * the object can not be adapted.
      *
      * @param object the object to adapt to <code>ResourceMapping</code>.
      * @return returns the adapted resource using the <code>IContributorResourceAdapter2</code>
      * or <code>null</code> if the Resources plug-in is not loaded.
      *
      * @since 3.1
      */
     public static Object getAdaptedContributorResourceMapping(Object object) {
         Class resourceMappingClass = LegacyResourceSupport.getResourceMappingClass();
         if (resourceMappingClass == null) {
             return null;
         }
         if (resourceMappingClass.isInstance(object)) {
             return null;
         }
         if (object instanceof IAdaptable) {
             IAdaptable adaptable = (IAdaptable) object;
             Class contributorResourceAdapterClass = LegacyResourceSupport.getIContributorResourceAdapterClass();
             if (contributorResourceAdapterClass == null) {
                 return adaptable.getAdapter(resourceMappingClass);
             }
             Class contributorResourceAdapter2Class = LegacyResourceSupport.getIContributorResourceAdapter2Class();
             if (contributorResourceAdapter2Class == null) {
                 return adaptable.getAdapter(resourceMappingClass);
             }
             Object resourceAdapter = adaptable.getAdapter(contributorResourceAdapterClass);
             Object resourceMappingAdapter;
             if (resourceAdapter != null && contributorResourceAdapter2Class.isInstance(resourceAdapter)) {
                 // The registered adapter also handles resource mappings
 resourceMappingAdapter = resourceAdapter;
             } else {
                 // Either there is no registered adapter or it doesn't handle resource mappings.
 // In this case, we will use the default contribution adapter
 resourceMappingAdapter = getDefaultContributorResourceAdapter();
                 if (resourceMappingAdapter == null) {
                     return null;
                 }
             }
             
            
                 // reflective equivalent of
 // result = ((IContributorResourceAdapter2) resourceAdapter).getAdaptedResource(adaptable);

              Method m = getContributorResourceAdapter2GetAdaptedResourceMappingMethod();
              if (m != null) {
                     
                 try {
                     Object result = m.invoke(resourceMappingAdapter, new Object []{adaptable});
                     if (result != null) {
                         return result;
                     }
                 } catch (IllegalArgumentException e) {
                      // shouldn't happen - but play it safe
 } catch (IllegalAccessException e) {
                      // shouldn't happen - but play it safe
 } catch (InvocationTargetException e) {
                      // shouldn't happen - but play it safe
 }
                     
              }
             
             
             // If we get here, that means that the object in question doesn't adapt to resource mapping
 // and it's contributed adapter doesn't do the adaptation either.
 // Before we fail, we will attempt to adapt the object to IResource and then to ResourceMapping
 Object r = getAdaptedContributorResource(object);
             if (r != null) {
                 return Platform.getAdapterManager().getAdapter(r, resourceMappingClass);
             }
             
             // we've exhausted every avenue so just return null
 return null;
         }
         // Fallback to querying the adapter manager directly when the object isn't an IAdaptable
 return Platform.getAdapterManager().getAdapter(object, resourceMappingClass);
     }
     
     /**
      * Adapts a selection to the given objectClass considering the Legacy resource
      * support. Non resource objectClasses are adapted using the <code>IAdapterManager</code>
      * and this may load the plug-in that contributes the adapter factory.
      * <p>
      * The returned selection will only contain elements successfully adapted.
      * </p>
      * @param selection the selection to adapt
      * @param objectClass the class name to adapt the selection to
      * @return an adapted selection
      *
      * @since 3.1
      */
     public static IStructuredSelection adaptSelection(IStructuredSelection selection, String objectClass) {
         List newSelection = new ArrayList (10);
         for (Iterator it = selection.iterator(); it.hasNext();) {
             Object element = it.next();
             Object adaptedElement = getAdapter(element, objectClass);
             if (adaptedElement != null) {
                 newSelection.add(adaptedElement);
             }
         }
         return new StructuredSelection(newSelection);
     }
     
     /**
      * Adapts an object to a specified objectClass considering the Legacy resource
      * support. Non resource objectClasses are adapted using the <code>IAdapterManager</code>
      * and this may load the plug-in that contributes the adapter factory.
      * <p>
      * The returned selection will be of the same size as the original, and elements that could
      * not be adapted are added to the returned selection as is.
      * </p>
      * @param element the element to adapt
      * @param objectClass the class name to adapt the selection to
      * @return an adapted element or <code>null</code> if the
      * element could not be adapted.
      *
      * @since 3.1
      */
     public static Object getAdapter(Object element, String objectClass) {
         Object adaptedElement = null;
         if (isInstanceOf(element.getClass(), objectClass)) {
             adaptedElement = element;
         } else {
             // Handle IResource
 if (LegacyResourceSupport.isResourceType(objectClass)) {
                 adaptedElement = getAdaptedResource(element);
             } else if (LegacyResourceSupport.isResourceMappingType(objectClass)) {
                 adaptedElement = getAdaptedResourceMapping(element);
                 if (adaptedElement == null) {
                     // The object doesn't adapt directly so check if it adapts transitively
 Object resource = getAdaptedResource(element);
                     if (resource != null) {
                         adaptedElement =( (IAdaptable)resource).getAdapter(LegacyResourceSupport.getResourceMappingClass());
                     }
                 }
             } else {
                 // Handle all other types by using the adapter factory.
 adaptedElement = Platform.getAdapterManager().loadAdapter(element, objectClass);
             }
         }
         return adaptedElement;
     }

     /**
      * Adapt the given element to an <code>IResource</code> using the following
      * search order:
      * <ol>
      * <li> using the IContributorResourceAdapter registered for the given element, or
      * <li> directly asking the element if it adapts.
      * </ol>
      *
      * @param element the element to adapt
      * @return an <code>IResource</code> instance if the element could be adapted or <code>null</code>
      * otherwise.
      * @since 3.1
      */
     public static Object getAdaptedResource(Object element) {
         Class resourceClass = LegacyResourceSupport.getResourceClass();
         Object adaptedValue = null;
         if (resourceClass != null) {
             if (resourceClass.isInstance(element)) {
                 adaptedValue = element;
             } else {
                 adaptedValue = LegacyResourceSupport.getAdaptedContributorResource(element);
             }
         }
         return adaptedValue;
     }

     /**
      * Adapt the given element to an <code>ResourceMapping</code> using the following
      * search order:
      * <ol>
      * <li> using the IContributorResourceAdapter2 registered for the given element, or
      * <li> directly asking the element if it adapts.
      * </ol>
      *
      * @param element the element to adapt
      * @return an <code>ResourceMapping</code> instance if the element could be adapted or <code>null</code>
      * otherwise.
      * @since 3.1
      */
     public static Object getAdaptedResourceMapping(Object element) {
         Class resourceMappingClass = LegacyResourceSupport.getResourceMappingClass();
         Object adaptedValue = null;
         if (resourceMappingClass != null) {
             if (resourceMappingClass.isInstance(element)) {
                 adaptedValue = element;
             } else {
                 adaptedValue = LegacyResourceSupport.getAdaptedContributorResourceMapping(element);
             }
         }
         return adaptedValue;
     }
     
     /**
      * Prevents construction
      */
     private LegacyResourceSupport() {
         // do nothing
 }

 }

